package model.constraints.inherited.configurationSet;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Vector;

import model.constraints.AbstractConstraint;
import model.constraints.inherited.Configuration;
import model.constraints.inherited.ConfigurationXDF;
import model.constraints.inherited.Inters;
import model.constraints.synthesized.attributes.SyntAttributesAbstract;
import model.constraints.synthesized.attributes.SyntAttributesXDF;
import automata.Alphabet;
import automata.Fsa;
import automata.Label;
import automata.State;
import automata.StateSet;

public class ConfigurationSetXDF extends ConfigurationSet {

	private static final long serialVersionUID = 1L;

	public ConfigurationSetXDF(){}
	
	public ConfigurationSetXDF(ArrayList<Configuration> a) {
		super.addAll(a);		
	}
	
	/**
	 * Checks if there's automaton-changing
	 * @param l label 
	 * @return
	 */
	public ConfigurationSet changeConfiguration(Label label) {		
		ConfigurationSet cs = new ConfigurationSetXDF();
		
		for (Configuration c : this) {
			Fsa fsa = c.getFsa(); 
			State state = c.getState();
			AbstractConstraint constraint = (AbstractConstraint) c.getConstraint();			
			
			addConfiguration(state, cs, label, fsa, constraint);//make transitions			
			//verify final state
			if (fsa.get_final_states().contains(state)) {
				int fposition = constraint.getFsaPosition(fsa);				

				//verify context position
				if (fposition == 1) {						
					//walks in left side automaton
					fsa = constraint.getSourceFsa();
					state = fsa.get_start_state();
					addConfiguration(state, cs, label, fsa, constraint);

					//walks in right side automaton
					fsa = constraint.getTargetFsa();
					state = fsa.get_start_state();
					addConfiguration(state, cs, label, fsa, constraint);										
				}
			}
		}
		
		//  HERE WE HAVE TO ORDER THE ENTIRE CONFIGURATION - IN CS
		// FIRST: CONTEXT automata, automata LEFT, RIGHT automata
		//                   SORT BY STATE		
		ArrayList<Configuration> a = new ArrayList<Configuration>(cs);
		Collections.sort(a);			
		
		ConfigurationSet csNew = new ConfigurationSetXDF(a);		

		return csNew;	
	}
	
	/**
	 * This functions creates a new configuration and inserts it set configuration
	 * @param State sf
	 * @param ConfigurationSet
	 * @param Label label
	 * @param Fsa	fsa
	 * @param Abstract Constraint 
	 */
	public void addConfiguration(State stateFsa, ConfigurationSet cs, Label label, Fsa fsa, AbstractConstraint constraint) {		
		Alphabet alphabet = fsa.get_alphabet();
		Label star = new Label("*");
		StateSet ss;
		
		if (alphabet.contains(label)) {			
			ss = fsa.get_dest_states(stateFsa, label);			
			for (State s: ss){								
				cs.add(new ConfigurationXDF(s, fsa, constraint));
			}
		}
		
		if (alphabet.contains(star)) {								
			if (fsa.get_dest_states(stateFsa, star) != null) {
			   ss = fsa.get_dest_states(stateFsa,star);  
			   
			   for (State s: ss) {
				   Configuration c = new ConfigurationXDF(s, fsa, constraint);
				   
				   if (!cs.contains(c))
					   cs.add(c);
			   }
		    }
		}
	}	

	public Vector<SyntAttributesAbstract> keyValidationData(Vector<SyntAttributesAbstract> syntVec, int k, String data, String posAnt, 
	   														String posAt, String nodeName,
	   														Hashtable<String,ArrayList<Inters>> htIntersXfd) 
	{
		// Validation for leaf nodes, considering their ancestor node.
		SyntAttributesAbstract syntAttAnt = new SyntAttributesXDF();
		SyntAttributesAbstract syntAtt = new SyntAttributesXDF();
		syntAtt.setNodeName(nodeName);
		syntAttAnt = (SyntAttributesAbstract) syntVec.elementAt(k);
		
		for (Configuration conf: this) {
			ConfigurationXDF confXDF = (ConfigurationXDF) conf;
			syntAtt =  confXDF.validateData(syntAtt, data, posAt, nodeName, htIntersXfd);			
			
			if(!syntAtt.isEmpty()) {
				if (!syntAttAnt.isEmpty()) {
					syntAttAnt.join(syntAtt, htIntersXfd,posAnt);
					syntVec.setElementAt(syntAttAnt,k);
				} else 
					syntVec.setElementAt(syntAtt,k);	  
			} else 
				syntVec.setElementAt(syntAttAnt,k);
		}
		
		System.err.println("Return SyntVec: "+syntVec.toString());
		return syntVec;
	}

	/**
	* This function make validation key in Node
	* 
	* @param syntVecAnt is Vector<SyntAttributes>
	* @param k is integer
	* @param syntAtt is SyntAttributes 
	* @param posAnt is String
	* @param posAt is String
	* @param nodeName is String
	* @param htContextXfd is HashTable
	* @param htIntersXfd is HashTable<String,ArrayList<Inters>> 
	* 
	* @return Vector SyntAttributes
	*/
	public Vector<SyntAttributesAbstract> keyValidationInNode(Vector<SyntAttributesAbstract> syntVecAnt, int k, SyntAttributesAbstract syntAtt,
			  													String posAnt, String posAt, String nodeName, Hashtable<String, Hashtable<ArrayList<String>, String>> htContextXfd, 
			  													Hashtable<String,ArrayList<Inters>> htIntersXfd, Hashtable<String, ArrayList<String>> hterror) {
	
		SyntAttributesAbstract syntAttAnt = (SyntAttributesAbstract) syntVecAnt.elementAt(k);  //Attributes from parent node, for the same xdf 
		
		for (Configuration conf: this) {
			ConfigurationXDF confXDF = (ConfigurationXDF) conf;
			SyntAttributesAbstract syntAttAux = new SyntAttributesXDF();
		
			syntAttAux = confXDF.validateInNode(syntAtt, syntAttAnt, posAnt, posAt, nodeName, htContextXfd, htIntersXfd, hterror);
		
		if(!syntAttAux.isEmpty())
			syntVecAnt.setElementAt(syntAttAux, k);			
		}
		
		System.err.println("Return In node "+ syntVecAnt.toString());
		return syntVecAnt;
	}
		

		
}
